#include "rbtree.h"
#include "modules.h"

#include "common.h"
#include "compmark.h"

static rbtree_t *module_tree = NULL;

static rbtree_t *instance_tree = NULL;

typedef struct indexed_modcallable {
	int		comp;
	int		idx;
	struct modcallable  *modulelist;
} indexed_modcallable;

static int module_instance_cmp(const void *one, const void *two)
{
	const module_instance_t *a = one;
	const module_instance_t *b = two;

	return strcmp(a->name, b->name);
}

static int indexed_modcallable_cmp(const void *one, const void *two)
{
	const indexed_modcallable *a = one;
	const indexed_modcallable *b = two;

	if (a->comp < b->comp) return -1;
	if (a->comp >  b->comp) return +1;

	return a->idx - b->idx;
}

/*
 *	Free a module instance.
 */
static void module_instance_free(void *data)
{
	module_instance_t *this = data;

	if (this->entry->module->detach) {
		(this->entry->module->detach)(this->insthandle);
	}


	if (this->mutex) {
		/*
		 *	FIXME
		 *	The mutex MIGHT be locked...
		 *	we'll check for that later, I guess.
		 */
		pthread_mutex_destroy(this->mutex);
		free(this->mutex);
	}


	memset(this, 0, sizeof(*this));
	free(this);
}


/*
 *	Compare two module entries
 */

static int module_entry_cmp(const void *one, const void *two)
{
	const module_entry_t *a = one;
	const module_entry_t *b = two;

	return strcmp(a->name, b->name);
}

/*
 *	Free a module entry.
 */
static void module_entry_free(void *data)
{
	module_entry_t *this = data;

	dlclose(this->handle);	/* ignore any errors */
	memset(this, 0, sizeof(*this));
	free(this);
}


/*
 *	Remove the module lists.
 */
int detach_modules(void)
{
	rbtree_free(instance_tree);
	rbtree_free(module_tree);

	return 0;
}


int scan_modules(const char *path) {
   struct dirent **namelist;

   int i,n,retval=0;
   char module_struct[STR_LEN];
   char *pbuff, *p;
   struct timeval tv;
   struct tm tm;

   void *handle;
   module_t *module;

   module_entry_t *node;

   int (*myfunc)(AFilter *);

   pbuff = Calloc(STR_LEN,char);

 while(1) {

   if ( ( n = scandir(path, &namelist, NULL, alphasort) ) < 0 ) 
   	ndebug("Scan Modules failed\n");
   else {

	while (n--) {

        if (strncmp(namelist[n]->d_name, "bfm_", 4) != 0 ) continue;

	sprintf(pbuff,"%s/%s",path,namelist[n]->d_name);
  	handle= dlopen(pbuff, RTLD_GLOBAL|RTLD_NOW);

  	if (!handle) {
      		ndebug("dlopen failed %s\n",dlerror());
		continue;
    	} 

	strcpy(module_struct, namelist[n]->d_name+4);
	p = strrchr(module_struct, '.');
	if (p) *p = '\0';

	module = dlsym(handle, module_struct);

      	if ( module!=NULL ) ndebug("link symbol %s Ok\n",module_struct);
      	else {
	    ndebug("Error check src %s for %s\n",pbuff,dlerror());
	    continue;
	}
	
	if (module->magic != BFM_MODULE_MAGIC_NUMBER) {
	     dlclose(handle);
      	     ndebug("Error magic %s\n",pbuff);
	     continue;
	    }
      	else ndebug("magic check %s correctly %s\n",pbuff,module_struct);

	node = (module_entry_t *)calloc(1,sizeof(*node));

	sprintf(node->name, "%s",pbuff);
	node->module = module;
	node->handle = handle;

	if ( (node->module->instantiate) &&
  	      ( (int (*)(void *))node->module->instantiate)( (void *)aFilter ) == FALSE ) {
		    dlclose(handle);
	    	free(node);
		continue;
	} 

	for( i =0 ; i<node->module->cnt; i++){
	    if ( node->module->methods[i].schmod == CMD_RESET ) continue;
	    funcMethod *fm=Calloc(1,funcMethod);
	    fm->schmod = node->module->methods[i].schmod;
	    fm->param = node->module->methods[i].param;
	    fm->fmethod = node->module->methods[i].fmethod;
	    ndebug("%s for %d %s method %d param %06x\n", 
            __FILE__,i,node->name,fm->schmod, fm->param);
	    		// setting cmd also
        if ( fm->fmethod == NULL ) continue;
	    if ( node->module->methods[i].schmod == CMD_RESET ) { 
           if ( fm->param >= CMD_MAX ) continue; 
	       listAddNodeTail(aFilter->sch_list[0].sched,(void *)fm);
           continue;
        } else {
           if ( fm->param >= SCH_MAX ) continue; 
	       listAddNodeTail(aFilter->sch_list[fm->schmod].sched,(void *)fm);
           }

    gettimeofday(&tv, NULL);
    if (localtime_r(&tv.tv_sec, &tm) != NULL) 
         switch(fm->schmod){
            case TICK: retval = 1; break;
			case 	MIN1: retval = tm.tm_min; break;
			case 	MIN2: retval = tm.tm_min/2; break;
			case 	MIN3: retval = tm.tm_min/3; break;
			case 	MIN5: retval = tm.tm_min/5; break;
			case 	MIN10: retval = tm.tm_min/10; break;
			case 	MIN15: retval = tm.tm_min/15; break;
			case 	MIN30: retval = tm.tm_min/30; break;
			case 	HOUR:  retval = tm.tm_hour; break;

			case 	ATTIME:
                    retval = fm->param;
                    break;

			case 	DAY: 
			case 	HIST:
            default:  
                    retval = 0;
                    break;
         };
	    aFilter->sch_list[fm->schmod].tickCount=retval;
	}

	if (!rbtree_insert(aFilter->module_tree, node)) {
		ndebug("Failed to cache module %s\n", node->name);
		dlclose(handle);
		free(node);
		continue;
	} else 
		ndebug("Cache module %s Ok\n", node->name);

	}

   }

   break;

 }
   free(pbuff);
   return(n);
}


int rescan_modules(char *path) {

    int retval = 0;

    return(retval);
}


int setup_modules(const char *path){
	int i;


        //FIXME this should be re-initalized when resan module or restart process

	// FIXME schedule list aFilter->sch_list is static SCH_MAX

	//    sch_list[i].sched = listCreate() ) == NULL ){

	if ( path != NULL) 
		scan_modules(path);

       ndebug("Scanning modules path %s DONE\n",path);
 	// after scan modules , must setup modules_instantiate

       return(0);
}
